# Python 趣味编程：第 2 章
# 练习问题 2.3 移动多个球
# --------------------------
# 程序名: ex02-3-balls.py

from tkinter import *
from dataclasses import dataclass
import time

# 给予初始值
DURATION = 0.001

@dataclass
class Ball:
    id: int
    x: int
    y: int
    d: int
    vx: int
    vy: int
    c: str

@dataclass
class Border:
    left: int
    right: int
    top: int
    bottom: int

# 将球绘制到初始位置，并返回生成的dataclass
def make_ball(x, y, d, vx, vy, c="black"):
    id = canvas.create_rectangle(x, y, x + d, y + d,
                                 fill=c, outline=c)
    # 因为只有d和c被赋予初始值，所以顺序被调换了
    return Ball(id, x, y, d, vx, vy, c)

# 球的移动:x和y各有位移
def move_ball( ball ):
    ball.x += ball.vx
    ball.y += ball.vy

# 从左上角的点和宽度、高度来绘制边框  
def make_walls(ox, oy, width, height):
    canvas.create_rectangle(ox, oy, ox+width, oy+height)

# 球(垫)的重新绘制 
def redraw_ball(ball):
    canvas.coords(ball.id, ball.x, ball.y,
                ball.x + ball.d, ball.y + ball.d)

tk = Tk()
canvas = Canvas(tk, width=800, height=600, bd=0)
canvas.pack()
tk.update()

# 给出墙壁的坐标(left, right, top, bottom)
border = Border(100, 700, 100, 500)

make_walls(
    border.left,
    border.top,
    border.right - border.left,
    border.bottom - border.top
    )

# 球的初始化
balls = [
    make_ball(100, 100, 20, 2, 1, "darkblue"),
    make_ball(200, 200, 25, -4, 3, "orange"),
    make_ball(300, 300, 10, -2, -1, "green"),
    make_ball(400, 400, 5, 4, 2, "darkgreen")
    ]

while True:
    for ball in balls:  # 处理所有的球
        move_ball(ball)  # 球的移动
        # 如果X坐标超过左右两面墙，则反转X的移动方向
        if (ball.x + ball.vx < border.left \
            or ball.x + ball.d >= border.right):
            ball.vx = -ball.vx
        # 如果Y坐标超过上下壁，则反转Y的移动方向
        if (ball.y + ball.vy < border.top \
            or ball.y + ball.d >= border.bottom):
            ball.vy = -ball.vy
        redraw_ball(ball)      # 球移动的绘制
    # 注意这里缩进的深度和for ball相同
    tk.update()              # 绘图反映在画面上
    time.sleep(DURATION)     # 在接下来的绘图之前，都要sleep
